package ws.performancetest.app.gui;

import com.microsoft.sqlserver.jdbc.SQLServerDriver;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultComboBoxModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import ws.utils.jdbc.DBConnection;
import ws.performancetest.app.gui.dialog.ErrorDialog;
import ws.performancetest.app.gui.strings.AppStrings;
import ws.performancetest.app.LoginInfo;
import ws.performancetest.app.Authentication;
import ws.utils.io.FileUtil;

/**
 * Graphical interface to connect to the database.
 * 
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public class DBConnectFrame extends javax.swing.JFrame {

  private static DBConnectFrame reference = new DBConnectFrame();
  /** ComboBox model for the driver combo box. */
  private DefaultComboBoxModel driverComboBoxModel;
  /** ComboBox model for the authentication combo box. */
  private DefaultComboBoxModel authenticationComboBoxModel;
  /** Folder that holds some configuration, like the last login. */
  private String confDir = "." + File.separator + "CONF" + File.separator;
  /** Name of the file that has the last login information. */
  private String lastLoginFile = confDir + LoginInfo.class.getName() + ".dat";
  private boolean running = false;

  /** Create new form DBConnectFrame */
  private DBConnectFrame() {
    /* Set look-and-feel to system. */
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (final ClassNotFoundException ex) {
      throw new RuntimeException(ex);
    } catch (final InstantiationException ex) {
      throw new RuntimeException(ex);
    } catch (final IllegalAccessException ex) {
      throw new RuntimeException(ex);
    } catch (final UnsupportedLookAndFeelException ex) {
      throw new RuntimeException(ex);
    }

    /* Initialize GUI components. */
    try {
      List<Driver> jdbcdrivers;
      // Uncomment next line to load every driver.
      // jdbcdrivers = Collections.list(DriverManager.getDrivers());
      
      // Load only SQLServerDriver drivers. Comment until empty line when loading every driver.
      Enumeration<Driver> enumd = DriverManager.getDrivers();
      jdbcdrivers = new ArrayList<Driver>();
      while(enumd.hasMoreElements()) {
        Driver d = enumd.nextElement();
        if(d instanceof SQLServerDriver) {
          jdbcdrivers.add(d);
        }
      }

      if (jdbcdrivers.isEmpty()) {
        throw new SQLException("Failed to find JDBC drivers. Please check classpath settings.");
      }

      driverComboBoxModel = new DefaultComboBoxModel(jdbcdrivers.toArray());
      if(System.getProperty("os.name").contains("Windows")) {
        authenticationComboBoxModel = new DefaultComboBoxModel(Authentication.toArray());
        // <editor-fold defaultstate="collapsed" desc="Add sqljdbc_auth.dll to java.library.path so Windows Authentication works.">
        String arch = System.getProperty("sun.arch.data.model");
        if (arch.equals("32")) {
          arch = "x86";
        } else {
          arch = "x64";
        }
        /* If we are running through NetBeans the path to sqljdbc_auth.dll is different. */
        if (System.getProperty("ws.run") != null && System.getProperty("ws.run").equals("nb")) {
          System.setProperty("java.library.path", System.getProperty("java.library.path") + ";../../Libraries/MSJDBC/auth/" + arch);
        } else {
          System.setProperty("java.library.path", System.getProperty("java.library.path") + ";./lib/jdbcauth/" + arch);
        }
        Field fieldSysPath;
        try {
          fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
          fieldSysPath.setAccessible(true);
          fieldSysPath.set(null, null);
          // <editor-fold defaultstate="collapsed" desc="Catch a lot of exceptions...">
        } catch (NoSuchFieldException ex) {
          Logger.getLogger(DBConnectFrame.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
          Logger.getLogger(DBConnectFrame.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
          Logger.getLogger(DBConnectFrame.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
          Logger.getLogger(DBConnectFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        // </editor-fold>
        // </editor-fold>
      } else {
        authenticationComboBoxModel = new DefaultComboBoxModel(new Object[]{Authentication.sql});
      }

      initComponents();
    } catch (final SQLException ex) {
      ErrorDialog.run(AppStrings.createExceptionMessage(ex));
      ex.printStackTrace();
      System.exit(1); // recovery doesn't seem possible.
    }

    /* Read last used login info. */
    LoginInfo loginInfo = null;
    try {
      loginInfo = (LoginInfo) FileUtil.readObject(lastLoginFile);
    } catch (final FileNotFoundException ex) {
      // it's OK to ignore. The file may not exist because it is the program
      // is running or it might have been erased by the user.
    } catch (final IOException ex) {
      // the program might continue its execution but the user will have
      //to re-enter login information.
      ex.printStackTrace();
    }
    if (loginInfo != null) {
      int idxDriver = 0;
      for (int i = 0; i < driverComboBoxModel.getSize(); ++i) {
        if (driverComboBoxModel.getElementAt(i).getClass().getName().equals(loginInfo.getDriver())) {
          idxDriver = i;
        }
      }
      cbxDriver.setSelectedIndex(idxDriver);
      txServer.setText(loginInfo.getServer());
      txPort.setText(loginInfo.getPort());
      txDatabase.setText(loginInfo.getDatabase());
      cbxAuthentication.setSelectedItem(loginInfo.getAuthentication());
      txUsername.setText(loginInfo.getUsername());
      txPassword.setText(loginInfo.getPassword());
    }

    /* Create conf dir, if doesn't exist. */
    File f = new File(confDir);
    if (!f.exists()) {
      f.mkdir();
    }

    System.setProperty("sql.tables.path", "../../SQL/Tables_and_Views");

    if (cbxAuthentication.getSelectedItem().toString().equals(Authentication.windows)) {
      txUsername.setEnabled(false);
      txPassword.setEnabled(false);
    } else {
      txUsername.setEnabled(true);
      txPassword.setEnabled(true);
    }
  }

  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    pnlMain = new javax.swing.JPanel();
    pnlLoginInformation = new javax.swing.JPanel();
    lbDriver = new javax.swing.JLabel();
    cbxDriver = new javax.swing.JComboBox();
    lbServer = new javax.swing.JLabel();
    txServer = new javax.swing.JTextField();
    lbDatabase = new javax.swing.JLabel();
    txDatabase = new javax.swing.JTextField();
    lbUsername = new javax.swing.JLabel();
    txUsername = new javax.swing.JTextField();
    lbPassword = new javax.swing.JLabel();
    txPassword = new javax.swing.JPasswordField();
    btConnect = new javax.swing.JButton();
    btCancel = new javax.swing.JButton();
    btWorkOffline = new javax.swing.JButton();
    txPort = new javax.swing.JTextField();
    jLabel1 = new javax.swing.JLabel();
    jLabel2 = new javax.swing.JLabel();
    cbxAuthentication = new javax.swing.JComboBox();
    jSeparator1 = new javax.swing.JSeparator();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle(AppStrings.AppName + " - " + AppStrings.menuConnectTo);
    setResizable(false);

    pnlLoginInformation.setBorder(javax.swing.BorderFactory.createTitledBorder("Login information"));

    lbDriver.setText("JDBC Driver");

    cbxDriver.setModel(driverComboBoxModel);

    lbServer.setText("Server");

    lbDatabase.setText("Database");

    lbUsername.setText("Username");

    txUsername.setEnabled(false);

    lbPassword.setText("Password");

    txPassword.setEnabled(false);

    btConnect.setText("Connect");
    btConnect.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        btConnectActionPerformed(evt);
      }
    });

    btCancel.setText("Cancel");
    btCancel.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        btCancelActionPerformed(evt);
      }
    });

    btWorkOffline.setText("Work Offline");
    btWorkOffline.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        btWorkOfflineActionPerformed(evt);
      }
    });

    jLabel1.setText("Port");

    jLabel2.setText("Authentication");

    cbxAuthentication.setModel(authenticationComboBoxModel);
    cbxAuthentication.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        cbxAuthenticationActionPerformed(evt);
      }
    });

    javax.swing.GroupLayout pnlLoginInformationLayout = new javax.swing.GroupLayout(pnlLoginInformation);
    pnlLoginInformation.setLayout(pnlLoginInformationLayout);
    pnlLoginInformationLayout.setHorizontalGroup(
      pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(pnlLoginInformationLayout.createSequentialGroup()
        .addContainerGap()
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(lbDriver)
          .addGroup(pnlLoginInformationLayout.createSequentialGroup()
            .addComponent(btConnect, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 137, Short.MAX_VALUE)
            .addComponent(btWorkOffline))
          .addGroup(pnlLoginInformationLayout.createSequentialGroup()
            .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(lbServer)
              .addComponent(lbDatabase)
              .addComponent(jLabel2)
              .addComponent(lbUsername)
              .addComponent(lbPassword))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(txPassword, javax.swing.GroupLayout.DEFAULT_SIZE, 326, Short.MAX_VALUE)
              .addComponent(txUsername, javax.swing.GroupLayout.DEFAULT_SIZE, 326, Short.MAX_VALUE)
              .addComponent(cbxAuthentication, 0, 326, Short.MAX_VALUE)
              .addComponent(cbxDriver, javax.swing.GroupLayout.Alignment.TRAILING, 0, 326, Short.MAX_VALUE)
              .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlLoginInformationLayout.createSequentialGroup()
                .addComponent(txServer, javax.swing.GroupLayout.DEFAULT_SIZE, 230, Short.MAX_VALUE)
                .addGap(18, 18, 18)
                .addComponent(jLabel1)
                .addGap(4, 4, 4)
                .addComponent(txPort, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE))
              .addComponent(txDatabase, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 326, Short.MAX_VALUE))))
        .addContainerGap())
      .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 420, Short.MAX_VALUE)
    );
    pnlLoginInformationLayout.setVerticalGroup(
      pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(pnlLoginInformationLayout.createSequentialGroup()
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(lbDriver)
          .addComponent(cbxDriver, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(txPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(jLabel1)
          .addComponent(txServer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(lbServer))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(txDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(lbDatabase))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(cbxAuthentication, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(jLabel2))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(txUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(lbUsername))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(txPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(lbPassword))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addGroup(pnlLoginInformationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(btConnect)
          .addComponent(btCancel)
          .addComponent(btWorkOffline)))
    );

    javax.swing.GroupLayout pnlMainLayout = new javax.swing.GroupLayout(pnlMain);
    pnlMain.setLayout(pnlMainLayout);
    pnlMainLayout.setHorizontalGroup(
      pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGap(0, 452, Short.MAX_VALUE)
      .addGroup(pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(pnlMainLayout.createSequentialGroup()
          .addContainerGap()
          .addComponent(pnlLoginInformation, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
          .addContainerGap()))
    );
    pnlMainLayout.setVerticalGroup(
      pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGap(0, 244, Short.MAX_VALUE)
      .addGroup(pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(pnlMainLayout.createSequentialGroup()
          .addContainerGap()
          .addComponent(pnlLoginInformation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
    );

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addComponent(pnlMain, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addComponent(pnlMain, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
    );

    pack();
  }// </editor-fold>//GEN-END:initComponents

  private void btWorkOfflineActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btWorkOfflineActionPerformed
    try {
      DBConnection.closeDBCon();
      reference.setVisible(false);
      Application.run();
    } catch (SQLException ex) {
      ErrorDialog.run(AppStrings.msgSQLException + ex.getMessage());
    }
  }//GEN-LAST:event_btWorkOfflineActionPerformed

  private void btCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btCancelActionPerformed
    if (running) {
      reference.setVisible(false);
    } else {
      System.exit(0);
    }
  }//GEN-LAST:event_btCancelActionPerformed

  private void btConnectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btConnectActionPerformed
    if (txDatabase.getText().isEmpty() || txServer.getText().isEmpty() || txPort.getText().isEmpty()
            || ((txUsername.getText().isEmpty() || txPassword.getPassword().length == 0) && cbxAuthentication.getSelectedItem().equals(Authentication.sql))) {
      ErrorDialog.run(AppStrings.msgParametersError);
    } else {
      Driver driver = (Driver) cbxDriver.getSelectedItem();
      String server = txServer.getText();
      String port = txPort.getText();
      String database = txDatabase.getText();
      String authentication = cbxAuthentication.getSelectedItem().toString();
      String username = txUsername.getText();
      String password = String.copyValueOf(txPassword.getPassword());
      try {
        DBConnection.closeDBCon();
        boolean flag = false;
        if (cbxAuthentication.getSelectedItem().equals(Authentication.sql)) {
          flag = DBConnection.setDBUrl(driver, server, port, database, username, password);
        } else {
          flag = DBConnection.setDBUrl(driver, server, port, database);
        }
        if (flag) {
          DBConnection.getDBCon();
          FileUtil.dumpObject(new LoginInfo(driver.getClass().getName(), server, port, database, authentication, username, password), lastLoginFile);
          reference.setVisible(false);
          Application.setServer(txServer.getText());
          Application.setPort(txPort.getText());
          Application.setUsername(txUsername.getText());
          Application.setPassword(new String(txPassword.getPassword()));
          Application.setDatabase(txDatabase.getText());
          Application.run();
          running = true;
          Application.setOKLabel("Connection established!");
        } else {
          ErrorDialog.run(AppStrings.msgURLFormatError);
        }
      } catch (Exception ex) {
        Logger.getLogger(DBConnectFrame.class.getName()).log(Level.SEVERE, null, ex);
        ErrorDialog.run(AppStrings.msgSQLException + ex.getMessage());
      }
    }
  }//GEN-LAST:event_btConnectActionPerformed

  private void cbxAuthenticationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbxAuthenticationActionPerformed
    if (cbxAuthentication.getSelectedItem().toString().equals(Authentication.windows)) {
      txUsername.setEnabled(false);
      txPassword.setEnabled(false);
    } else {
      txUsername.setEnabled(true);
      txPassword.setEnabled(true);
    }
  }//GEN-LAST:event_cbxAuthenticationActionPerformed

  public static void run() {
    reference.setVisible(true);
  }

  /**
   * @param args the command line arguments
   */
  public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(new Runnable() {

      public void run() {
        DBConnectFrame.run();
      }
    });
  }
  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton btCancel;
  private javax.swing.JButton btConnect;
  private javax.swing.JButton btWorkOffline;
  private javax.swing.JComboBox cbxAuthentication;
  private javax.swing.JComboBox cbxDriver;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JSeparator jSeparator1;
  private javax.swing.JLabel lbDatabase;
  private javax.swing.JLabel lbDriver;
  private javax.swing.JLabel lbPassword;
  private javax.swing.JLabel lbServer;
  private javax.swing.JLabel lbUsername;
  private javax.swing.JPanel pnlLoginInformation;
  private javax.swing.JPanel pnlMain;
  private javax.swing.JTextField txDatabase;
  private javax.swing.JPasswordField txPassword;
  private javax.swing.JTextField txPort;
  private javax.swing.JTextField txServer;
  private javax.swing.JTextField txUsername;
  // End of variables declaration//GEN-END:variables
}
